package com.xcity.game.peakroad;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.slf4j.Logger;

import com.xcity.db.entity.Player;
import com.xcity.game.combat.Combat;
import com.xcity.game.combat.Combat.State;
import com.xcity.game.combat.Combat.Type;
import com.xcity.game.combat.CombatException;
import com.xcity.game.combat.CombatService;
import com.xcity.game.combat.CombatTeam;
import com.xcity.game.combat.Round;
import com.xcity.game.combat.attr.CombatAttributeType;
import com.xcity.game.combat.listener.CombatListener;
import com.xcity.game.combat.unit.CombatUnit;
import com.xcity.game.combat.unit.Combatable;
import com.xcity.game.combat.unit.impl.EmptyCombatUnit;
import com.xcity.game.common.Cause;
import com.xcity.game.common.GameStaticConfig;
import com.xcity.game.common.ItemResponse;
import com.xcity.game.common.MessageConst;
import com.xcity.game.event.GameEvents;
import com.xcity.game.formation.Formation;
import com.xcity.game.item.ItemInfo;
import com.xcity.game.item.ItemService;
import com.xcity.game.item.ItemTemplate;
import com.xcity.game.item.drop.Drop;
import com.xcity.game.item.drop.DropGroup;
import com.xcity.game.item.drop.listener.MultiDropAdapter;
import com.xcity.game.npc.NpcCombatable;
import com.xcity.game.npc.NpcTeamTemplate;
import com.xcity.game.npc.NpcTemplate;
import com.xcity.game.peakroad.combat.PeakRoadCombatBuilder;
import com.xcity.game.peakroad.combat.PeakRoadCombatUnit;
import com.xcity.game.peakroad.combat.PeakRoadCombatable;
import com.xcity.game.peakroad.model.PeakRoadInstance;
import com.xcity.game.peakroad.model.response.Floor;
import com.xcity.game.peakroad.template.PeakRoadTemplate;
import com.xcity.pomelo.push.msg.CombatEndPush;
import com.xcity.pomelo.request.CombatInfoRequest;
import com.xcity.pomelo.request.CombatPeakRoadRequest;
import com.xcity.pomelo.response.CombatInfoResponse;
import com.xcity.pomelo.response.CombatInfoResponse.TeamBuilder;
import com.xcity.pomelo.response.CombatResponse;

import naga.x.App;
import naga.x.game.template.TemplateService;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Packet;
import naga.x.net.Session;

/**
 * 爬塔（巅峰之路）战斗监听
 * @author yang.li
 *
 */
public class PeakRoadCombatListener implements CombatListener {

	private Player player;
	private PeakRoadTemplate template;
	private int diff; // 难度系数
	private List<PeakRoadCombatable> npcs;
	private long combatBeginTime;
	
	protected static final Logger LOG = CombatService.LOG;
	
	@Override
	public void roundStart(Round round) {
	}

	@Override
	public void roundEnd(Round round) {
	}
	
	private void validate(Player player, int diff) throws CombatException {
		PeakRoadInstance instance = player.getPeakRoadInstance();
		if (instance.hasState(PeakRoadInstance.STATE_CHALLENGE_MASK)) {
			throw new CombatException(MessageConst.INSTANCE_PEAK_ROAD_REACHED_TOP_FLOOR);
		}
		if (!instance.hasState(PeakRoadInstance.STATE_PREVIOUS_CHALLENGE_MASK) && PeakRoadUtils.getLeftTimes(player) <= 0) {
			throw new CombatException(MessageConst.INSTANCE_PEAK_ROAD_JOINED_TIMES_MAXED);
		}
		PeakRoadTemplate template = PeakRoadUtils.getTemplate(instance.getFloor());
		if (template == null) {
			throw new CombatException(MessageConst.ILLEGAL_OPERATION);
		}
		if (template.getType() != PeakRoadTemplate.FLOOR_CHALLENGE) {
//			throw new CombatException(MessageConst.INSTANCE_PEAK_ROAD_CHALLENGE_REWARD_FLOOR);
			throw new CombatException(MessageConst.INSTANCE_PEAK_ROAD_REACHED_TOP_FLOOR);
		}
		NpcTeamTemplate npcTeam = template.getNpcTeam(diff);
		if (npcTeam == null) {
			throw new CombatException(MessageConst.ILLEGAL_OPERATION);
		}
		this.player = player;
		this.template = template;
		this.diff = diff;
	}

	@Override
	public void prepareCombat(Session session, CombatInfoRequest packet) throws CombatException {
		Player player = session.getClient();
		validate(player, packet.getPeakRoadDiffCoefficient());
		
		PeakRoadInstance instance = player.getPeakRoadInstance();
		NpcTeamTemplate npcTeam = template.getNpcTeam(diff);
		
		boolean allDied = true;
		PeakRoadCombatable playerLeader = instance.findUnit(player);
		TeamBuilder playerTeamBuilder = new TeamBuilder();
		if (playerLeader.getHp() > 0) {
			allDied = false;
			playerTeamBuilder.setLeader(playerLeader);
		}
		PeakRoadCombatable[] employees = instance.getFormationUnits();
		for (int i = 0; i < employees.length; i++) {
			PeakRoadCombatable employee = employees[i];
			if (employee != null && employee.getHp() > 0) {
				allDied = false;
				playerTeamBuilder.addMember(employee);
			} else {
				playerTeamBuilder.addMember((Combatable) null);
			}
		}
//		if (allDied) {
//			throw new CombatException(MessageConst.FORMATION_EMPTY);
//		}
		
		TeamBuilder npcTeamBuilder = new TeamBuilder();
//		List<PeakRoadCombatable> lastNpcs = instance.getLastNpcUnits();
//		if (lastNpcs != null && lastNpcs.size() > 0) {
//			int size = lastNpcs.size() < CombatTeam.DEFAULT_TEAM_CAPACITY ? lastNpcs.size() : CombatTeam.DEFAULT_TEAM_CAPACITY - 1;
//			for (int i = 0; i < size; i++) {
//				npcTeamBuilder.addMember(lastNpcs.get(i));
//			}
//			// the boss in the last position
//			int pos = CombatTeam.DEFAULT_TEAM_CAPACITY - 1;
//			if (pos < lastNpcs.size()) {
//				npcTeamBuilder.setLeader(lastNpcs.get(pos));
//			}
//		} else {
		// 对战界面直接取模板信息，不取缓存
			NpcTemplate boss = npcTeam.getBoss();
			if (boss != null) {
//				npcTeamBuilder.setLeader(new PeakRoadCombatable((NpcCombatable) boss.toCombatabale()));
				npcTeamBuilder.setLeader(boss.toCombatabale());
			}
			NpcTemplate[] npcs = npcTeam.getNpcs();
			for (NpcTemplate npc : npcs) {
//				npcTeamBuilder.addMember(npc != null ? new PeakRoadCombatable((NpcCombatable) npc.toCombatabale()) : null);
				npcTeamBuilder.addMember(npc != null ? npc.toCombatabale() : null);
			}
//		}
		
		session.send(new CombatInfoResponse(packet.getSerial(), playerTeamBuilder.build(), npcTeamBuilder.build(), Formation.TYPE_PEAK_ROAD));
	}

	@Override
	public CombatResponse create(CombatService service, Type type, Session session, Packet packet) throws CombatException {
		CombatPeakRoadRequest req = (CombatPeakRoadRequest) packet;
		Player player = session.getClient();
		validate(player, req.getDiff());
		
		PeakRoadInstance instance = player.getPeakRoadInstance();
		NpcTeamTemplate npcTeam = template.getNpcTeam(diff);
		
		// build己方战斗数据
		boolean allDied = true;
		PeakRoadCombatable playerLeader = instance.findUnit(player);
		PeakRoadCombatBuilder builder = new PeakRoadCombatBuilder().setListener(this);
		if (playerLeader.getHp() > 0) {
			allDied = false;
			builder.setPlayer(playerLeader);
		}
		PeakRoadCombatable[] employees = instance.getFormationUnits();
		for (int i = 0; i < employees.length; i++) {
			PeakRoadCombatable employee = employees[i];
			if (employee != null && employee.getHp() > 0) {
				allDied = false;
				builder.addEmployee(employee);
			}
		}
		if (allDied) {
			throw new CombatException(MessageConst.FORMATION_EMPTY);
		}
		
		instance.enter(diff); // 重置某些数据
		builder.setPlayerBuffs(instance.getBuffs());
		// build npc战斗数据
		List<PeakRoadCombatable> lastNpcs = instance.getLastNpcUnits();
		if (lastNpcs != null && lastNpcs.size() > 0) {
			int size = lastNpcs.size() < CombatTeam.DEFAULT_TEAM_CAPACITY ? lastNpcs.size() : CombatTeam.DEFAULT_TEAM_CAPACITY - 1;
			for (int i = 0; i < size; i++) {
				builder.addNpc(lastNpcs.get(i));
			}
			// the boss in the last position
			int pos = CombatTeam.DEFAULT_TEAM_CAPACITY - 1;
			if (pos < lastNpcs.size()) {
				builder.setBossNpc(lastNpcs.get(pos));
			}
			this.npcs = lastNpcs;
		} else {
			this.npcs = new ArrayList<PeakRoadCombatable>(CombatTeam.DEFAULT_TEAM_CAPACITY);
			NpcTemplate[] npcs = npcTeam.getNpcs();
			for (NpcTemplate npc : npcs) {
				PeakRoadCombatable c = npc != null ? new PeakRoadCombatable((NpcCombatable) npc.toCombatabale()) : null;
				builder.addNpc(c);
				this.npcs.add(c);
			}
			// 补齐空位
			int n = Formation.FORMATION_CAPCITY - this.npcs.size();
			while (--n >= 0) {
				this.npcs.add(null);
			}
			NpcTemplate boss = npcTeam.getBoss();
			PeakRoadCombatable c = boss != null ? new PeakRoadCombatable((NpcCombatable) boss.toCombatabale()) : null;
			builder.setBossNpc(c);
			this.npcs.add(c);
		}
		PeakRoadUtils.addChallengeTimes(player, 1);
		this.combatBeginTime = TimeUpdater.getInstance().now();
		Combat combat = builder.build();
		LOG.info("[PEAKROAD]ID[{}]FLOOR[{}]DIFF[{}]TIMES[{}]", player.getId(), instance.getFloor(), diff, instance.getJoinedTimes());
		return new CombatResponse(req.getSerial(), combat);
	}

	@Override
	public void end(Combat combat, boolean notify) {
		doSync(combat);
		PeakRoadInstance instance = player.getPeakRoadInstance();
		byte result = combat.getState() == State.SOURCE_WON ? Combat.SUCC : Combat.FAIL;
		CombatEndPush push = new CombatEndPush(result, Combat.Type.PEAK_ROAD, combat.getId());
		if (result == Combat.SUCC) {
			instance.fireChallengeSucceeded(template);
			CombatResult ref = new CombatResult();
			ref.leftTimes = PeakRoadUtils.getLeftTimes(player);
			ref.star = calcStars(combat);
			ref.rewards = doReward();
			push.setRef(ref);
			App.getApp().getEventManager().addEvent(GameEvents.EVENT_PEAK_ROAD_WIN, player, template);
		} else {
			instance.fireChallengeFailed(template);
			CombatResult ref = new CombatResult();
			ref.leftTimes = PeakRoadUtils.getLeftTimes(player);
			push.setRef(ref);
		}
		if (notify) {
			player.send(push);
		}
		LOG.info("[PEAKROAD END]ID[{}]FLOOR[{}]DIFF[{}]TIMES[{}]RESULT[{}]", player.getId(), template.getId(), diff, instance.getJoinedTimes(), result);
	}
	
	private CombatReward doReward() {
		final CombatReward reward = new CombatReward();
		if (template.getPeakCoin() > 0 && player.addPeakCoin(template.getPeakCoin(), Cause.PEAK_ROAD)) {
			reward.peakCoin = template.getPeakCoin();
		}
		DropGroup drop = template.getDropGroup(diff);
		if (drop != null) {
			drop.drop(Drop.RND, player, Cause.PEAK_ROAD, new MultiDropAdapter() {
				@Override
				public void itemDroped(Player player, int itemId, int count) {
					reward.addItem(itemId, count);
				}
				@Override
				public void expDroped(Player player, int val) {
				}
				@Override
				public void goldDroped(Player player, int gold) {
//					ItemTemplate template = ItemTemplate.defaultGoldTemplate;
//					if (template != null) {
//						reward.addItem(template, gold);
//					} else {
//					}
					reward.gold = gold;
				}
				@Override
				public void stoneDroped(Player player, int stone) {
					ItemTemplate template = ItemService.stoneDefault;
					if (template != null) {
						reward.addItem(template, stone);
					} else {
					}
					reward.stone = stone;
				}
			});
		}
		return reward;
	}
	
	private void doSync(Combat combat) {
		PeakRoadInstance instance = player.getPeakRoadInstance();
		// 同步己方单位数据
		List<CombatUnit> playerTeamUnits = combat.getSource().all();
		for (int i = 0; i < playerTeamUnits.size(); i++) {
			CombatUnit u = playerTeamUnits.get(i);
			if (u != EmptyCombatUnit.INSTANCE) {
				PeakRoadCombatUnit unit = (PeakRoadCombatUnit) u;
				doSync(unit, unit.refer());
			}
		}
		// 同步敌方单位数据（失败才同步）
		if (combat.getState() != State.SOURCE_WON) {
			List<CombatUnit> npcTeamUnits = combat.getTarget().all();
			for (int i = 0, size = npcTeamUnits.size() < this.npcs.size() ? npcTeamUnits.size() : this.npcs.size(); i < size; i++) {
				CombatUnit u = npcTeamUnits.get(i);
				if (u != EmptyCombatUnit.INSTANCE) {
					PeakRoadCombatable c = this.npcs.get(i);
					doSync(u, c);
				}
			}
			instance.setLastNpcUnits(npcs);
		} else {
			instance.setLastNpcUnits(null);
		}
	}
	
	private void doSync(CombatUnit src, PeakRoadCombatable dst) {
		if (dst != null) {
			dst.setAttributeValue(CombatAttributeType.HP, src.getHp());
//			if (dst.getType() == Combatable.PLAYER) {
//				player.send(new ComplexSync(SyncPush.SYNC_PEAK_ROAD_UNIT_HP, -1, src.getHp()));
//			} else if (dst.getType() == Combatable.EMPLOYEE) {
//				player.send(new ComplexSync(SyncPush.SYNC_PEAK_ROAD_UNIT_HP, src.getId(), src.getHp()));
//			}
		}
	}
	
	/**
	 * 战斗评星
	 * @return
	 */
	private int calcStars(Combat combat) {
		int star = 3;
		CombatTeam team = combat.getSource();
		List<CombatUnit> units = team.all();
		int leftHp = 0; // 剩余血量
		int maxHp = 0; // 总血量
		int diedCount = 0; // 死亡单位数量
		for (int i = units.size() - 1; i >= 0; i--) {
			CombatUnit u = units.get(i);
			maxHp += u.getMaxHp();
			leftHp += u.getHp();
			if (u.isDied()) {
				++diedCount;
			}
		}
		// condition 1: 剩余血量百分比
		int condLeftHp = template.getCondLeftHp();
		if (condLeftHp > 0 && leftHp < condLeftHp * maxHp / GameStaticConfig.percentBase) {
			--star;
		}
		// condition 2: 死亡单位数量 
		int condDiedCount = template.getCondDiedCount();
		if (condDiedCount >= 0 && diedCount > condDiedCount) {
			--star;
		}
		// condition 3: 回合数
		int condRounds = template.getCondRounds();
		if (condRounds > 0 && combat.roundCount() > condRounds) {
			--star;
		}
		// conditon 4: 时间（秒）
		int condTimeElapse = template.getCondTimeElapse();
		if (condTimeElapse > 0 && (TimeUpdater.getInstance().now() - combatBeginTime) > condTimeElapse * 1000) {
			--star;
		}
		if (star < 1) {
			star = 1;
		}
		return star;
	}
	
	public static class CombatResult {
		public int leftTimes; // 剩余挑战次数
		public int star; // 星级(0~3)
		public CombatReward rewards; // 挑战胜利的奖励
	}
	
	public static class CombatReward {
		public int gold; // 奖励现金
		public int stone; // 奖励钻石
		public int peakCoin; // 奖励巅峰币
		public List<ItemInfo> items = Collections.emptyList(); // 奖励物品
		public Floor next; // 下一层信息
		public void addItem(int id, int count) {
			ItemTemplate template = App.getApp().getServiceManager().get(TemplateService.class).get(ItemTemplate.class, id);
			addItem(template, count);
		}
		public void addItem(ItemTemplate template, int count) {
			if (items.isEmpty()) {
				items = new ArrayList<ItemInfo>();
			}
			ItemInfo item = null;
			for (int i = items.size() - 1; i >= 0; i--) { // filter duplicates
				ItemInfo it = items.get(i);
				if (it.getId() == template.getId()) {
					item = it;
					break;
				}
			}
			if (item == null) {
				item = new ItemResponse(template, 0);
				items.add(item);
			}
			item.setCount(item.getCount() + count);
		}
	}

}
